#include "ServerPacketListener.h"
#include "externals/includes/osc/OscReceivedElements.h"
#include "externals/includes/osc/OscPacketListener.h"
#include "externals/includes/ip/UdpSocket.h"
#include "stdio.h"
#include "QStringList"

ServerPacketListener::ServerPacketListener(OSCthulhuServer* server) {
    this->server = server;
}

void ServerPacketListener::ProcessMessage( const osc::ReceivedMessage& m,
                                const IpEndpointName& remoteEndpoint )
    {
        try{
            // example of parsing single messages. osc::OsckPacketListener
            // handles the bundle traversal.
            //cout<<"Received an OSC Message"<<endl;
            if( strcmp( m.AddressPattern(), "/test1" ) == 0 ){
                // example #1 -- argument stream interface
                osc::ReceivedMessageArgumentStream args = m.ArgumentStream();
                bool a1;
                osc::int32 a2;
                float a3;
                const char *a4;
                args >> a1 >> a2 >> a3 >> a4 >> osc::EndMessage;

                qDebug() << "received '/test1' message with arguments: "
                    << a1 << " " << a2 << " " << a3 << " " << a4;

            }else if( strcmp( m.AddressPattern(), "/test2" ) == 0 ){
                // example #2 -- argument iterator interface, supports
                // reflection for overloaded messages (eg you can call
                // (*arg)->IsBool() to check if a bool was passed etc).
                osc::ReceivedMessage::const_iterator arg = m.ArgumentsBegin();
                bool a1 = (arg++)->AsBool();
                int a2 = (arg++)->AsInt32();
                float a3 = (arg++)->AsFloat();
                const char *a4 = (arg++)->AsString();
                if( arg != m.ArgumentsEnd() )
                    throw osc::ExcessArgumentException();

                qDebug() << "received '/test2' message with arguments: "
                    << a1 << " " << a2 << " " << a3 << " " << a4;
            }else if( strcmp( m.AddressPattern(), "/login" ) == 0 ){
                osc::ReceivedMessageArgumentStream args = m.ArgumentStream();
                const char *serverPassword;
                const char *userName;
                const char *userPassword;
                float port;
                args >> serverPassword >> userName >> userPassword >> port  >> osc::EndMessage;

                char buffer [50];
                remoteEndpoint.AddressAndPortAsString(buffer);

                QString address(buffer);
                QString publicPort(buffer);
                address = ((QStringList)(address.split(":"))).at(0);
                publicPort = ((QStringList)(publicPort.split(":"))).at(1);
                //port = portS.toFloat();
                qDebug() << "received '/login' message with arguments: "
                        << serverPassword <<" "<< userName << " " << userPassword << " " << address << " "<< port << " " << publicPort;
                this->server->createUser(serverPassword,userName,userPassword,address,port,publicPort);
            } else if( strcmp( m.AddressPattern(), "/logout" ) == 0 ){
                osc::ReceivedMessageArgumentStream args = m.ArgumentStream();
                const char *serverPassword;
                const char *userName;
                const char *userPassword;
                float port;
                args >> serverPassword >> userName >> userPassword >> port  >> osc::EndMessage;

                qDebug() << "received '/logout' message with arguments: "
                    << serverPassword <<" "<< userName << " " << userPassword << " " << port << " ";
                server->disconnectUser(serverPassword,userName,userPassword,port);
            } else if( strcmp( m.AddressPattern(), "/alive" ) == 0 ){
                osc::ReceivedMessageArgumentStream args = m.ArgumentStream();
                const char *serverPassword;
                const char *userName;
                const char *userPassword;
                float port;
                args >> serverPassword >> userName >> userPassword >> port  >> osc::EndMessage;

                //std::cout << "received '/alive' message with arguments: "
                //    << serverPassword <<" "<< userName << " " << userPassword << " " << port << " " << endl;
                server->setUserAlive(QString(userName));
            } else if( strcmp( m.AddressPattern(), "/aO" ) == 0 ){
                osc::ReceivedMessage::const_iterator arg = m.ArgumentsBegin();
                //std::cout << "Message type tags: "<< m.TypeTags() << std::endl;
                int i = 0;
                QString name,type,subtype,argName;
                QString password = QString((arg++)->AsString());

                bool newObject;
                while(arg != m.ArgumentsEnd()) {
                    switch(i){
                    case 0:
                        name = QString((arg++)->AsString());
                        break;
                    case 1:
                        type = QString((arg++)->AsString());
                        break;
                    case 2:
                        subtype = QString((arg++)->AsString());
                        newObject = server->addSyncObject(name,type,subtype);
                        break;
                    default :
                            if(newObject) {

                            switch(m.TypeTags()[i+1]) {
                            case 'i':
                                if(newObject) {
                                    server->addSyncArg(name,new QVariant((int)(arg++)->AsInt32()));
                            }
                            break;
                            case 'f':
                                if(newObject) {
                                    server->addSyncArg(name,new QVariant((arg++)->AsFloat()));
                                }
                                break;
                            case 'd':
                                if(newObject) {
                                    server->addSyncArg(name,new QVariant((arg++)->AsDouble()));
                                }
                                break;
                            case 's':
                                if(newObject) {
                                    server->addSyncArg(name,new QVariant(QString((arg++)->AsString())));
                                }
                                break;
                            }
                        } else {
                            arg++;
                        }
                        break;
                    }
                    i++;
                }

                if(newObject) {
                    if(password.compare(server->getServerPassword()) == 0) {
                        server->finalizeSyncObject(name);
                    }
                }
                if( arg != m.ArgumentsEnd() )
                    throw osc::ExcessArgumentException();
            } else if( strcmp( m.AddressPattern(), "/sA" ) == 0 ){
                osc::ReceivedMessage::const_iterator arg = m.ArgumentsBegin();
                QVariant* qvar;
                QString inputSync = "none";
                QString password = QString((arg++)->AsString());
                QString name = QString((arg++)->AsString());
                int argNum = (arg++)->AsInt32();
                switch(m.TypeTags()[3]) {
                case 'i':
                    qvar = new QVariant((int)(arg++)->AsInt32());
                    //server->setSyncArg(name,argNum,new QVariant((int)(arg++)->AsInt32()));
                    break;
                case 'f':
                    qvar = new QVariant((arg++)->AsFloat());
                    //server->setSyncArg(name,argNum,new QVariant((arg++)->AsFloat()));
                    break;
                case 'd':
                    qvar = new QVariant((float)(arg++)->AsDouble());
                    //server->setSyncArg(name,argNum,new QVariant((float)(arg++)->AsDouble()));
                    break;
                case 's':
                    qvar = new QVariant(QString((arg++)->AsString()));
                    //server->setSyncArg(name,argNum,new QVariant(QString((arg++)->AsString())));
                    break;
                }
                while(arg != m.ArgumentsEnd()) {
                    inputSync = QString((arg++)->AsString());
                }
                if(password.compare(server->getServerPassword()) == 0) {
                    server->setSyncArg(name,argNum,qvar,inputSync);
                }
            } else if( strcmp( m.AddressPattern(), "/rO" ) == 0 ){
                osc::ReceivedMessageArgumentStream args = m.ArgumentStream();
                const char *serverPassword;
                const char *name;
                float port;
                args >> serverPassword >> name>> osc::EndMessage;

                //std::cout << "received '/removeSyncObject' message with arguments: "
                //    << serverPassword <<" "<< name << " " << endl;
                if(QString(serverPassword).compare(server->getServerPassword())==0) {
                    server->removeSyncObject(QString(name));
                }
            } else if( strcmp( m.AddressPattern(), "/sC" ) == 0 ){
                //CHAT
                osc::ReceivedMessage::const_iterator arg = m.ArgumentsBegin();
                QString password = QString((arg++)->AsString());
                QString uN = QString( arg++->AsString() );    // cache username
                // have the server multicast the message to everyone but the owner of the message
                if(password.compare(server->getServerPassword()) == 0) {
                    server->multicastChatToUsers( m, uN );
                }

            } else if( strcmp( m.AddressPattern(), "/sChangePiece" ) == 0 ){
                osc::ReceivedMessage::const_iterator arg = m.ArgumentsBegin();
                QString password = QString((arg++)->AsString());
                QString user = QString((arg++)->AsString());
                QString piece = QString((arg++)->AsString());

                if(password.compare(server->getServerPassword()) == 0) {
                    server->changeUserPiece(user,piece);
                }
            } else if( strcmp( m.AddressPattern(), "/cleanup" ) == 0 ){
                osc::ReceivedMessage::const_iterator arg = m.ArgumentsBegin();
                QString password = QString((arg++)->AsString());
                QString user = QString((arg++)->AsString());
                QString piece = QString((arg++)->AsString());

                if(password.compare(server->getServerPassword()) == 0) {
                    server->cleanup(user,piece);
                }
            }
        }catch( osc::Exception& e ){
            // any parsing errors such as unexpected argument types, or
            // missing arguments get thrown as exceptions.
            qDebug() << "error while parsing message: "
                << m.AddressPattern() << ": " << e.what();
        }
    }
